#ifndef ENGINE_H
#define ENGINE_H

#include <memory>
#include <vector>
#include <string>
#include <iostream>
#include <stdint.h>


// A PlayerMask is used to control access to game state; each player has a bit in the
//  mask.  The first bit is reserved for the server, which may have information that
//  no players are allowed to see.
#define MAX_PLAYERS 31
typedef uint32_t PlayerMask;
const PlayerMask HIDDEN = 0;
const PlayerMask SERVERMASK = 0x1;


// A State is a set of information about the game state: e.g. CamperState for the position
//  and characteristics of an individual camper, MapState for the playing field, etc.
// Each piece of information that should only be visible to a subset of players should
//  have a PlayerMask associated with it.
// The server has the definitive copy of the game state; each client has a partial copy,
//  containing only the information that its player is allowed to know.  Computing a
//  client copy from the server copy is easy: just AND the State's PlayerMasks with the
//  mask for the client, then zero out any data whose associated PlayerMask ends up being
//  0 == HIDDEN.  The masks serve a dual purpose: in the server copy, they describe which
//  players have access to some data; in a client copy, they describe whether the client
//  has the data or not.
// All implementers should have a default constructor and a copy constructor (presumably just
//  the default ones, most of the time).
struct State
{
	// Destroy information that the specified mask does not have access to.  (To get a
	//  client version of some State from the server version, just make a copy and then
	//  censor the copy.)
	virtual void censor(const PlayerMask& mask) = 0;

	// Get a string that uniquely represents the type of an implementing subclass.
	//  Prefixes for States should begin with "#".
	virtual std::string getPrefix() const = 0;

	// Get a text representation of the data; it must begin with the prefix from getPrefix().
	virtual void serialize(std::ostream& ostr) const = 0;

	// Get a text representation of a client version of the data.  This is equivalent
	//  to making a copy, censoring the copy, and then serializing the copy, but
	//  implementers may be able to do it more efficiently than that.
	virtual void serialize(const PlayerMask& mask, std::ostream& ostr) const = 0;

	// Construct from a text representation; if the text cannot be understood,
	//  this object's state becomes undefined and the istream's fail bit is set.
	virtual void unserialize(std::istream& istr) = 0;
};

// macro to provide declarations of State interface functions, for readability
#define DECLARE_STATE(PREFIX) \
	virtual void censor(const PlayerMask& mask); \
	virtual std::string getPrefix() const {return PREFIX;} \
	virtual void serialize(std::ostream& ostr) const; \
	virtual void serialize(const PlayerMask& mask, std::ostream& ostr) const; \
	virtual void unserialize(std::istream& istr);



// A StateChange represents a change in the game state (e.g. a camper moving to a different
//  spot on the map).  Each StateChange should contain all the information required to
//  obtain the State object(s) to be modified and to perform the modification, in either
//  direction.
// Example: to move a camper, we need an identifier for the camper (so we can obtain the
//  camper's State from the game object), the old coordinates of the camper, and the new
//  coordinates.  To apply the change, we fetch the State and set the new coords; to undo
//  the change, we fetch the State and set the old coords.
// The purpose of these guys is to have an easily-saved log of what has happened in the
//  game, and to allow clients to rewind/fast-forward the action to study moves.  They
//  shouldn't contain any rule logic; they should just be purely mechanical operations
//  on the game state--change this value from X to Y, etc.
// Implementers should have default and copy constructors.
// Access to the State objects to be modified is typically through a std::function provided
//  by the Game object (which owns all the States and StateChanges).
struct StateChange
{
	virtual void apply() = 0;
	virtual void undo() = 0;

	// Same semantics as State::censor.
	virtual void censor(const PlayerMask& mask) = 0;

	// determine whether any meaningful data is contained (i.e. if anything remains
	//  post-censoring)
	virtual bool isEmpty() const = 0;

	// Prefixes for StateChanges should begin with "$".
	virtual std::string getPrefix() const = 0;

	// Same semantics as their State counterparts.
	virtual void serialize(std::ostream& ostr) const = 0;
	virtual void serialize(const PlayerMask& mask, std::ostream& ostr) const = 0;
	virtual void unserialize(std::istream& istr) = 0;
};

// macro to provide declarations of StateChange interface functions, for readability
#define DECLARE_STATE_CHANGE(PREFIX) \
	virtual void apply(); \
	virtual void undo(); \
	virtual void censor(const PlayerMask& mask); \
	virtual bool isEmpty() const; \
	virtual std::string getPrefix() const {return PREFIX;} \
	virtual void serialize(std::ostream& ostr) const; \
	virtual void serialize(const PlayerMask& mask, std::ostream& ostr) const; \
	virtual void unserialize(std::istream& istr);

// utility for combining many conceptually-linked StateChanges into one
struct MultiStateChange : public StateChange
{
	std::vector<std::shared_ptr<StateChange> > changes;

	std::function<std::shared_ptr<StateChange> (const std::string&)> factory;

	DECLARE_STATE_CHANGE("$MultiStateChange")
};


// Client and server States and StateChanges are expected to maintain the following
//  invariants (which can be checked by automatic means):
//
// 1. For any State or StateChange in a valid state (i.e. one that hasn't been trashed
//     by a failed unserialization or something), the result of (serialize -> unserialize
//     -> serialize) must be exactly the same as the result of a single serialization.
// 2. Values protected by a HIDDEN mask are undefined and may not affect serialization.
//     If two states in memory are identical except for the HIDDEN portions of their
//     data, their serializations must be identical.
// 3. If State S1' is a censored version of S1, StateChange C' is a censored version of
//     C, and both censorships are performed with the same mask, then the result of
//     applying C' to S1' must be identical to the result of applying C to S1 and then
//     censoring.
//
//     C            S1   ---apply C--->   S2
//     |            |                     |
//   censor       censor                censor
//     |            |                     |
//     v            v                     v
//     C'           S1'  ---apply C'-->   S2'
//
//    ...In other words, the server and client must stay in sync.  At the start of the
//     game, the client gets a censored copy of the server's game state; from that point
//     on, the server applies uncensored state changes to its state, while the client
//     applies censored changes to its state.  Nonetheless, their states are synchronized--
//     if, at any time, the client were to throw out its state and request a fresh
//     censored copy of the state from the server, it would receive a state identical to
//     the one it tossed out.
//    In other other words, although the client only needs to receive diffs from the
//     server when something happens, the effect is the same as if the server sent an
//     entire copy every time.




// The GameHistory holds the complete log of everything that has transpired in the game.
//  For servers, this is mainly useful only for producing log files, but for clients, this
//  allows rewinding/fast-forwarding of the action, in order to study opponents' moves, etc.
// A client's UI only needs to be able to depict the current game state; in order to show
//  some past event, the game state itself can simply be rewound to that point.  UI for
//  player input can be enabled/disabled based on whether the GameHistory is at its end
//  (if you're looking at the most up-to-date state, and it's your turn, you can move).
class GameHistory
{
public:
	// Get the current position in the history; i.e. what point in time the current
	//  game state is a snapshot of.
	unsigned int getTimePos() const {return position;}

	// Move the game state to a specified time position (return false if the position is out
	//  of range).  If the desired position is later than the current one, applies all
	//  changes in between the two; if it's earlier, undoes all changes between the two.
	bool setTimePos(unsigned int pos);

	bool isAtEnd() const {return position == changes.size();}
	void advanceToEnd();

	// Adds a StateChange to the end of the history.  Does *not* apply the change to
	//  the game state (not even if we were already at the end); position must be
	//  advanced manually.
	virtual bool appendChange(std::shared_ptr<StateChange> change);

private:
	std::vector<std::shared_ptr<StateChange> > changes;
	unsigned int position;
};


// An Action is a message sent from client to server, describing a move (or other action)
//  that the player wishes to make.  (Examples: switch camper X to crawling mode; move
//  camper Y to point Z; end turn; etc.)
// Implementers should have default and copy constructors.
// Unlike StateChanges, Actions are essentially just pure data; we don't really need them
//  to be classes.  But this way provides some extra coding safety; we can say something
//  like "new CamperModeChange(camperID, CamperMode::CRAWLING)" instead of creating a
//  generic message object and filling it with #defined constants like MSG_CAMPERMODECHANGE.
//  (The former allows us to type-check both the CamperMode and possibly the camperID.)
struct Action
{
	// Prefixes for Actions should begin with "@".
	virtual std::string getPrefix() const = 0;
	virtual void serialize(std::ostream& ostr) const = 0;
	virtual void unserialize(std::istream& istr) = 0;
};


// A Game object contains the State objects which hold the game state, has methods which
//  implement the rules of the game, and provides an interface for the front end (UI) to
//  obtain information about (and to manipulate) the game state.
// (Probably this should be broken up a little bit--there could be separate client and server
//  subclasses, perhaps.)
class Game
{
public:
	// See whether an Action represents a legal move; if it does, return any state changes
	//  that result from it.  Return an empty vector if the Action is illegal.
	virtual std::vector<std::shared_ptr<StateChange> > validateAction(std::shared_ptr<Action> action) const = 0;

	// Since StateChanges and Actions are communicated between client and server using
	//  text messages, the receiver needs a factory method to create an object from the text.
	// These guys should create an object of the class specified by the prefix, then
	//  unserialize the text into the object, returning NULL if either of these steps fail.
	virtual std::shared_ptr<StateChange> stateChangeFactory(const std::string& prefix) = 0;
	virtual std::shared_ptr<Action> actionFactory(const std::string& prefix) = 0;
};


// A message between client and server that has nothing to do with the actual game; things like informing the
//  client of its player number, (possibly) text chat, etc.
// Since the low-level client/server communication will probably not need to be worked on by a large number
//  of people, we don't need the super-paranoid verbosity that Actions have; a plain old string should be fine.
//  Probably this doesn't even need to be a class.
struct ControlMessage
{
	// message should begin with "%"
	std::string msg;
};


// A Client object represents the whole of the back end portion of a client; it contains the game state
//  and history, and handles communication with the server.
class Client
{
public:
	// Called when a message comes in from the server; determines what kind of message it is and
	//  dispatches it.
	void receiveMessage(const std::string& str);

	void receiveControlMessage(const ControlMessage& msg);

	// Adds a just-arrived state change to the game history.
	void receiveStateChange(std::shared_ptr<StateChange> change);

private:
	std::shared_ptr<Game> game;
	GameHistory history;
};


// A Server object contains the server's copy of the game state and history, and handles communication
//  with the clients.
class Server
{
public:
	// Called when a message comes in from a client; determines what kind of message it is and
	//  dispatches it.  Also validates the source of the message before dispatching--makes sure
	//  that Actions marked as coming from player 1 were actually received from player 1's client, etc.
	void receiveMessage(const std::string& str, int clientID);

	void receiveControlMessage(const ControlMessage& msg);

	// Asks the Game to validate a received action.  If it is accepted:
	//  1. applies the resulting state changes to our copy of the game state
	//  2. obtains censored versions of the state changes for individual players
	//  3. dispatches the censored state changes to the clients
	void receiveAction(std::shared_ptr<Action> action);

private:
	std::shared_ptr<Game> game;
	GameHistory history;
};


#endif // ENGINE_H